﻿/* 
 *  <copyright file="FlatList3DFarseerScene.cs" company="Wesley Jowitt">
 *  
 *  SobrietyEngine: Copyright (c) 2011 Wesley Jowitt
 *  
 *  This software is distributed under a software license.
 *   * As of the creation time of this file, the license in use is the Microsoft Permissive License (Ms-PL) 
 *    * The license can be viewed at:
 *     * License.Txt in the root folder of this project
 *      * http://sobrietyengine.codeplex.com/license
 *       * http://www.microsoft.com/opensource/licenses.mspx
 *       
 *  </copyright>
 *   */

using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using SobrietyEngine.Components;
using SobrietyEngine.Components.Camera;
using SobrietyEngine.Components.Graphic;
using SobrietyEngine.Physics;

namespace SobrietyEngine.Scene
{
    public class FlatList3DFarseerScene: BaseScene, IScene
    {
        GraphicsDevice device;
        PresentationParameters pp;

        bool drawGui = true;
        RenderTarget2D guiRenderTarget;
        Texture2D guiBuffer;

        bool drawPhysics = true;
        RenderTarget2D physicsRenderTarget;
        Texture2D physicsBuffer;

        bool drawScene = true;
        RenderTarget2D scene3DRenderTarget;
        Texture2D scene3DBuffer;

        protected FarseerPhysicsService physicsGameService;
        public string PhysicsWorldName { get; set; } 

        

        public FlatList3DFarseerScene(string sceneName, Game game, string physWorldName, Vector2 gravity)
            : base(sceneName, game)
        {
            device = game.GraphicsDevice;
            pp = device.PresentationParameters;
            guiRenderTarget = new RenderTarget2D(device, pp.BackBufferWidth, pp.BackBufferHeight, false,
                                            device.DisplayMode.Format, device.PresentationParameters.DepthStencilFormat);

            physicsRenderTarget = new RenderTarget2D(device, pp.BackBufferWidth, pp.BackBufferHeight, false,
                                device.DisplayMode.Format, device.PresentationParameters.DepthStencilFormat);

            scene3DRenderTarget = new RenderTarget2D(device, pp.BackBufferWidth, pp.BackBufferHeight, false,
                                device.DisplayMode.Format, DepthFormat.Depth24Stencil8);//device.PresentationParameters.DepthStencilFormat);

            physicsGameService = (FarseerPhysicsService)game.Services.GetService(typeof(IPhysicsGameService));
            PhysicsWorldName = physWorldName;

            if (!physicsGameService.Worlds.ContainsKey(physWorldName))
            {
                Logging.Logger.GetSingleton().WriteDebug("FlatList3DFarserScene() creaing physics world " + physWorldName);
                physicsGameService.AddWorld(physWorldName, gravity);
            }
        }

        public override void Remove()
        {
            //clears actors
            this.Clear();

            //delete the farseer scene
            physicsGameService.RemoveWorld(PhysicsWorldName);
        }

        public FlatList3DFarseerScene(string sceneNameAndPhysicsName, Game game, Vector2 gravity)
            : this(sceneNameAndPhysicsName, game, sceneNameAndPhysicsName, gravity)
        {

        }

        public override void Clear()
        {
            //base handles clearing actors and components
            base.Clear();

            //we have added phsycis in there, so lets clear that
            physicsGameService.RemoveWorld(PhysicsWorldName);
        }


        //full screen effect is just for playing with for now.
        Effect fullscreenEffect = null;
        static float timer = 0;
        public override void ManualDraw(GameTime gameTime)
        {
            if (!ComponentsByIfaceType.ContainsKey(typeof(ICamera)))
                return;

            if (drawScene)
            {
                //do 3d stuff
                //now do the 3d objects
                //set some spriteBacth values back (just in case)
                device.BlendState = BlendState.AlphaBlend;
                device.DepthStencilState = DepthStencilState.Default;

                device.SetRenderTarget(scene3DRenderTarget);
                //device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
                device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer| ClearOptions.Stencil, new Color(0, 0, 0, 1), 1.0f, 0);

                foreach (ICamera cam in ComponentsByIfaceType[typeof(ICamera)])
                {
                    if (!cam.Active) continue;
                    Camera3DCmp c3d = cam as Camera3DCmp;
                    if (c3d == null) continue;

                    Matrix transform = cam.GetViewMatrix();
                    graphicsDevice.GraphicsDevice.Viewport = cam.Viewport;

                    //if (CreateDebugViews)
                    //    physicsGameService.Draw(gameTime, cam.ProjectionMatrix, cam.GetViewMatrix());

                    if (ComponentsByIfaceType.ContainsKey(typeof(IGraphic3D)))
                    {
                        List<SobrietyEngine.Components.IComponent> gc = ComponentsByIfaceType[typeof(IGraphic3D)];
                        for (int a = 0; a < gc.Count; ++a)
                        {
                            //if (cam.IsInCamera((Base3DGraphic)gc[a]))
                            {
                                ((Base3DGraphic)gc[a]).UpdateGraphic(gameTime);
                                ((Base3DGraphic)gc[a]).Draw( cam, cam.ProjectionMatrix, cam.GetViewMatrix());
                            }
                        }
                    }
                }
                device.SetRenderTarget(null);
                scene3DBuffer = (Texture2D)scene3DRenderTarget;
            }

            if (drawPhysics)
            {
                //physics debug view
                if (Debug)
                {
                    device.SetRenderTarget(physicsRenderTarget);
                    device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, new Vector4(0, 0, 0, 0.3f), 1.0f, 0);
                    foreach (ICamera cam in ComponentsByIfaceType[typeof(ICamera)])
                    {
                        if (!cam.Active) continue;
                        Camera3DCmp c3d = cam as Camera3DCmp;
                        if (c3d == null) continue;

                        Matrix transform = cam.GetViewMatrix();
                        graphicsDevice.GraphicsDevice.Viewport = cam.Viewport;

                        physicsGameService.Draw(PhysicsWorldName, gameTime, cam.ProjectionMatrix, cam.GetViewMatrix());
                    }
                    device.SetRenderTarget(null);
                    physicsBuffer = (Texture2D)physicsRenderTarget;
                }

            }


            if (drawGui)
            {
                //now 2D sprite batch scene
                //used for gui, or for 2d scene.
                //may need to scaleCmp positions to fit?
                device.SetRenderTarget(guiRenderTarget);
                device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, new Vector4(0, 0, 0, 0.3f), 1.0f, 0);

                foreach (ICamera cam in ComponentsByIfaceType[typeof(ICamera)])
                {
                    if (!cam.Active)
                        continue;
                    Camera2DCmp c2d = cam as Camera2DCmp;
                    if (c2d == null) continue;

                    Matrix transform = cam.GetViewMatrix();
                    graphicsDevice.GraphicsDevice.Viewport = cam.Viewport;


                    spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, null, null, null, null, transform);

                    if (ComponentsByIfaceType.ContainsKey(typeof(IGraphic2D)))
                    {
                        List<SobrietyEngine.Components.IComponent> gc = ComponentsByIfaceType[typeof(IGraphic2D)];
                        for (int a = 0; a < gc.Count; ++a)
                        {
                           // if (cam.IsInCamera((Base2DGraphic)gc[a]))
                            //{
                                ((Base2DGraphic)gc[a]).UpdateGraphic(gameTime);
                                ((Base2DGraphic)gc[a]).Draw(spriteBatch);
                         //   }
                        }
                    }
                    spriteBatch.End();
                }

                device.SetRenderTarget(null);
                guiBuffer = (Texture2D)guiRenderTarget;
            }

            //draw all scenes to the screen, alpha blended
            //if (fullscreenEffect == null)
            //    fullscreenEffect = gameServices.Content.Load<Effect>("Shaders/wiggle");

            spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, null, null, null, fullscreenEffect, Matrix.Identity);

            /*
            if (false)//fullscreenEffect != null)
            {
                timer += (float)gameTime.ElapsedGameTime.Milliseconds / 500;
                if (timer > 4)
                    timer -= 6.28f;
                fullscreenEffect.Parameters["fTimer"].SetValue(timer);
            }*/

            if (scene3DBuffer != null)
                spriteBatch.Draw(scene3DBuffer, new Vector2(0, 0), null, Color.White, 0, new Vector2(0, 0), 1.0f, SpriteEffects.None, 1);
            if (physicsBuffer != null)
                spriteBatch.Draw(physicsBuffer, new Vector2(0, 0), null, Color.White, 0, new Vector2(0, 0), 1.0f, SpriteEffects.None, 1);
            if (guiBuffer != null)
                spriteBatch.Draw(guiBuffer, new Vector2(0, 0), null, Color.White, 0, new Vector2(0, 0), 1.0f, SpriteEffects.None, 1);

            spriteBatch.End();
        }
    }
}
